package erfgame.core.world.terrain.async;

import java.util.concurrent.Executor;
import java.util.logging.Logger;

import erfgame.core.dao.ObjectDAO;

public class AsyncObjectDAOProxy<KeyType, ObjectType> implements ObjectDAO<KeyType, ObjectType> {

	@SuppressWarnings("unused")
	private static final Logger log = Logger.getLogger( AsyncObjectDAOProxy.class.getName() ); 
	
	private Executor executor;
	private ObjectDAO<KeyType, ObjectType> proxied;
	private ObjectDAO<KeyType, ObjectType> waitingObjects;
	
	public AsyncObjectDAOProxy( 
			Executor executor, 
			ObjectDAO<KeyType, ObjectType> proxied, 
			ObjectDAO<KeyType, ObjectType> waitingObjects 
	) {
		this.executor = executor;
		this.proxied = proxied;
		this.waitingObjects = waitingObjects;
	}
	
	public boolean containsObject(KeyType key) {
		return this.waitingObjects.containsObject( key ) || proxied.containsObject( key );
	}

	public boolean removeObject(KeyType key) {
		boolean a;
		synchronized( this ) {
			a = waitingObjects.removeObject(key);
		}
		boolean b = proxied.removeObject(key);
		return a || b;
	}

	public ObjectType retrieveObject(KeyType key) {
		ObjectType result = waitingObjects.retrieveObject(key);
		if( result == null ) {
			result = proxied.retrieveObject(key);
		}
		return result;
	}

	public void storeObject(KeyType key, ObjectType object) {
		synchronized( this ) {
			boolean added; 
			added = !waitingObjects.containsObject(key);
			waitingObjects.storeObject(key, claim(object));
			if( added ) {
				executor.execute( new Storer( key ) );
			}
		}
	}
	
	protected ObjectType claim(ObjectType o) {
		return o;
	}
	
	protected void free(ObjectType o) {
		
	}
	
	private class Storer implements Runnable{
		private KeyType key;
		
		public Storer( KeyType key ) {
			this.key = key;
		}

		public void run() {
			synchronized( AsyncObjectDAOProxy.this ) {
				ObjectType object = waitingObjects.retrieveObject(key);
				if( object != null ) {
					proxied.storeObject(key, object);
					waitingObjects.removeObject(key);
					free(object);
				}
			}
		}

		@Override
		@SuppressWarnings("unchecked")
		public boolean equals(Object obj) {
			if( obj != null && obj.getClass().equals( this.getClass() ) ) {
				return ((Storer) obj).key.equals( this.key );
			} else {
				return false;
			}
		}

		@Override
		public int hashCode() {
			return key.hashCode();
		}
	}
}
